# Copyright (c) 2013-2015 The University of Manchester, UK.
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#  * Redistributions of source code must retain the above copyright notice,
#    this list of conditions and the following disclaimer.
#
#  * Redistributions in binary form must reproduce the above copyright notice,
#    this list of conditions and the following disclaimer in the documentation
#    and/or other materials provided with the distribution.
#
#  * Neither the names of The University of Manchester nor the names of its
#    contributors may be used to endorse or promote products derived from this
#    software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Author: Robert Haines

require 'test/unit'
require 'ucf'

# A class to test the overriding of reserved and managed names.
class NewUCF < UCF::File

  private_class_method :new

  def initialize(filename)
    super(filename)
    register_managed_entry(ZipContainer::ManagedDirectory.new("src"))
    register_managed_entry(ZipContainer::ManagedDirectory.new("test"))
    register_managed_entry(ZipContainer::ManagedDirectory.new("lib"))
    register_managed_entry(ZipContainer::ManagedFile.new("index.html"))

    register_reserved_name("reserved_dir")
  end

end

class TestReservedNames < Test::Unit::TestCase

  # Check that the reserved names verify correctly.
  def test_verify_reserved_name
    assert(NewUCF.verify?($ucf_example))

    assert_nothing_raised(ZipContainer::MalformedContainerError) do
      NewUCF.verify!($ucf_example)
    end
  end

  # Check the reserved names stuff all works correctly, baring in mind that
  # such comparisons for UCF documents should be case sensitive.
  def test_reserved_names
    UCF::File.open($ucf_example) do |ucf|
      assert_equal(1, ucf.reserved_names.length)
      assert_equal(["mimetype"], ucf.reserved_names)
      assert_equal(6, ucf.managed_files.length)
      assert_equal(["META-INF/container.xml",
        "META-INF/manifest.xml", "META-INF/metadata.xml",
        "META-INF/signatures.xml", "META-INF/encryption.xml",
        "META-INF/rights.xml"], ucf.managed_file_names)
      assert(ucf.reserved_entry?("mimetype"))
      assert(ucf.reserved_entry?("mimetype/"))
      assert(ucf.reserved_entry?("MimeType"))
      assert(ucf.managed_entry?("META-INF/container.xml"))
      assert(ucf.managed_entry?("META-INF/manifest.xml"))
      assert(ucf.managed_entry?("MeTa-INF/maniFest.XML"))

      assert_equal(1, ucf.managed_directories.length)
      assert_equal(["META-INF"], ucf.managed_directory_names)
      assert(ucf.managed_entry?("META-INF"))
      assert(ucf.managed_entry?("META-INF/"))
      assert(ucf.managed_entry?("MeTa-iNf"))
      assert(ucf.managed_entry?("MeTa-iNf/"))
      refute(ucf.reserved_entry?("META-INF"))

      assert_equal(7, ucf.managed_entries.length)
      assert_equal(["META-INF/container.xml",
        "META-INF/manifest.xml", "META-INF/metadata.xml",
        "META-INF/signatures.xml", "META-INF/encryption.xml",
        "META-INF/rights.xml", "META-INF"], ucf.managed_entry_names)

      refute(ucf.managed_entry?("This_should_fail"))
      refute(ucf.managed_entry?("META_INF"))
      refute(ucf.managed_entry?("META_INF/"))
    end
  end

  # Check that overriding the reserved names in a sub-class works correctly
  def test_subclass_reserved_names
    NewUCF.open($ucf_example) do |ucf|
      assert_equal(2, ucf.reserved_names.length)
      assert_equal(["mimetype", "reserved_dir"], ucf.reserved_names)
      assert_equal(7, ucf.managed_files.length)
      assert_equal(["index.html", "META-INF/container.xml",
        "META-INF/manifest.xml", "META-INF/metadata.xml",
        "META-INF/signatures.xml", "META-INF/encryption.xml",
        "META-INF/rights.xml"], ucf.managed_file_names)
      assert(ucf.reserved_entry?("mimetype"))
      assert(ucf.reserved_entry?("mimetype/"))
      assert(ucf.reserved_entry?("MimeType"))
      assert(ucf.managed_entry?("index.html"))
      assert(ucf.managed_entry?("Index.HTML"))
      refute(ucf.reserved_entry?("index.html"))

      assert_equal(4, ucf.managed_directories.length)
      assert_equal(["META-INF", "src", "test", "lib"],
        ucf.managed_directory_names)
      assert(ucf.managed_entry?("META-INF"))
      assert(ucf.managed_entry?("META-INF/"))
      assert(ucf.managed_entry?("MeTa-iNf"))
      assert(ucf.managed_entry?("src"))
      assert(ucf.managed_entry?("SRC"))
      assert(ucf.managed_entry?("test"))
      assert(ucf.managed_entry?("lib"))
      assert(ucf.managed_entry?("lIb/"))
      refute(ucf.reserved_entry?("META-INF"))
      refute(ucf.reserved_entry?("src"))
      refute(ucf.reserved_entry?("test"))
      refute(ucf.reserved_entry?("lib"))

      assert_equal(11, ucf.managed_entries.length)
      assert_equal(["index.html", "META-INF/container.xml",
        "META-INF/manifest.xml", "META-INF/metadata.xml",
        "META-INF/signatures.xml", "META-INF/encryption.xml",
        "META-INF/rights.xml", "META-INF", "src", "test", "lib"],
        ucf.managed_entry_names)

      refute(ucf.managed_entry?("This_should_fail"))
      refute(ucf.managed_entry?("META_INF"))
      refute(ucf.managed_entry?("META_INF/"))
      refute(ucf.managed_entry?("index.htm"))
    end
  end

  # Check that nothing happens when trying to delete the mimetype file.
  def test_delete_mimetype
    UCF::File.open($ucf_example) do |ucf|
      assert(ucf.file.exists?("mimetype"))
      assert_nil(ucf.remove("mimetype"))
      assert(ucf.file.exists?("mimetype"))
    end
  end

  # Check that nothing happens when trying to rename the mimetype file.
  def test_rename_mimetype
    UCF::File.open($ucf_example) do |ucf|
      assert(ucf.file.exists?("mimetype"))
      assert_nil(ucf.rename("mimetype", "something-else"))
      assert(ucf.file.exists?("mimetype"))
      refute(ucf.file.exists?("something-else"))
    end
  end

  # Check that nothing happens when trying to replace the contents of the
  # mimetype file.
  def test_replace_mimetype
    UCF::File.open($ucf_example) do |ucf|
      assert(ucf.file.exists?("mimetype"))
      assert_nil(ucf.replace("mimetype", $zip_empty))
      assert_equal("application/epub+zip", ucf.file.read("mimetype"))
    end
  end

  # Check that an exception is raised when trying to add file with a reserved
  # name.
  def test_add_reserved
    UCF::File.open($ucf_empty) do |ucf|
      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.add("META-INF", $zip_empty)
      end
    end
  end

  # Check that an exception is raised when trying to add file with a reserved
  # name to a subclassed container.
  def test_subclass_add_reserved
    NewUCF.open($ucf_empty) do |ucf|
      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.add("mimetype", $zip_empty)
      end

      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.add("reserved_dir", $zip_empty)
      end

      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.add("MimeType", $zip_empty)
      end

      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.add("Reserved_Dir", $zip_empty)
      end
    end
  end

  # Check that the META-INF directory is detected as non-existent when trying
  # to delete it.
  def test_delete_metainf
    UCF::File.open($ucf_example) do |ucf|
      assert_raises(Errno::ENOENT) do
        ucf.remove("META-INF")
      end
    end
  end

  # Check that the META-INF directory is detected as non-existent when trying
  # to rename it.
  def test_rename_metainf
    UCF::File.open($ucf_example) do |ucf|
      assert_raises(Errno::ENOENT) do
        ucf.rename("META-INF", "something-else")
      end
    end
  end

  # Check that an exception is raised when trying to create a directory with a
  # reserved name.
  def test_mkdir_reserved
    UCF::File.open($ucf_empty) do |ucf|
      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.mkdir("mimetype")
      end

      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.mkdir("META-INF/container.xml")
      end
    end
  end

  # Check that an exception is raised when trying to create a directory with a
  # reserved name in a subclassed container.
  def test_subclass_mkdir_reserved
    NewUCF.open($ucf_empty) do |ucf|
      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.mkdir("mimetype")
      end

      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.mkdir("index.html")
      end

      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.mkdir("reserved_dir")
      end

      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.mkdir("Reserved_Dir")
      end

      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.mkdir("META-INF/container.xml")
      end
    end
  end

  # Check that a file cannot be renamed to one of the reserved names.
  def test_rename_to_reserved
    UCF::File.open($ucf_example) do |ucf|
      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.rename("dir/code.rb", "mimetype")
      end
    end
  end

  # Check that a file cannot be renamed to one of the reserved names in a
  # subclassed container.
  def test_subclass_rename_to_reserved
    NewUCF.open($ucf_example) do |ucf|
      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.rename("dir/code.rb", "mimetype")
      end

      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.rename("dir", "reserved_dir")
      end
    end
  end

  # Check that the ruby-like File and Dir classes respect reserved and managed
  # names.
  def test_file_dir_ops_reserved
    UCF::File.open($ucf_empty) do |ucf|
      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.file.open("META-INF", "w") do |f|
          f.puts "TESTING"
        end
      end

      assert_nothing_raised(ZipContainer::ReservedNameClashError) do
        ucf.file.open("mimetype") do |f|
          assert_equal("application/epub+zip", f.read)
        end
      end

      assert_nothing_raised(ZipContainer::ReservedNameClashError) do
        ucf.file.delete("mimetype")
        assert(ucf.file.exists?("mimetype"))
      end
    end
  end

  # Check that the ruby-like File and Dir classes respect reserved names in a
  # subclassed container.
  def test_subclass_file_dir_ops_reserved
    NewUCF.open($ucf_empty) do |ucf|
      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.file.open("META-INF", "w") do |f|
          f.puts "TESTING"
        end
      end

      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.file.open("TEST", "w") do |f|
          f.puts "TESTING"
        end
      end

      assert_nothing_raised(ZipContainer::ReservedNameClashError) do
        ucf.file.open("mimetype") do |f|
          assert_equal("application/epub+zip", f.read)
        end
      end

      assert_nothing_raised(ZipContainer::ReservedNameClashError) do
        ucf.file.delete("mimetype")
        assert(ucf.file.exists?("mimetype"))
      end

      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.dir.mkdir("index.html")
      end

      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.dir.mkdir("reserved_dir")
      end

      assert_raises(ZipContainer::ReservedNameClashError) do
        ucf.dir.mkdir("Reserved_Dir")
      end
    end
  end

end
